#include "SimpleCalculation.h"

#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>

EFI_GUID gEfiSimpleCalculationProtocolGuid = EFI_SIMPLE_CALCULATION_PROTOCOL_GUID;

#define SIMPLE_CALCULATION_PRIVATE_DATA_SIGNATURE  SIGNATURE_32('S','C','A','L')

typedef struct 
{
  UINTN                            Signature;
  EFI_SIMPLE_CALCULATION_PROTOCOL  SimpleCalculationProtocol;
  UINTN                            FirstOperand;
  UINTN                            SecondOperand;
  UINTN                            Result;
} SIMPLE_CALCULATION_PRIVATE_DATA;

STATIC SIMPLE_CALCULATION_PRIVATE_DATA gSimpleCalculationPrivateData;

#define SIMPLE_CALCULATION_PRIVATE_DATA_FROM_THIS(a)  BASE_CR(a, SIMPLE_CALCULATION_PRIVATE_DATA, SimpleCalculationProtocol)

EFI_STATUS
EFIAPI
Add(IN EFI_SIMPLE_CALCULATION_PROTOCOL *This, IN UINTN A, IN UINTN B, OUT UINTN *Result)
{
	SIMPLE_CALCULATION_PRIVATE_DATA *PrivateData = SIMPLE_CALCULATION_PRIVATE_DATA_FROM_THIS(This);

	PrivateData->FirstOperand  = A;
	PrivateData->SecondOperand = B;
	PrivateData->Result        = A + B;
	*Result                    = PrivateData->Result;

	Print(L"Add: %u + %u = %u\n", PrivateData->FirstOperand, PrivateData->SecondOperand, PrivateData->Result);

	return EFI_SUCCESS;
}

EFI_STATUS
EFIAPI
Subtract(IN EFI_SIMPLE_CALCULATION_PROTOCOL *This, IN UINTN A, IN UINTN B, OUT UINTN *Result)
{
	SIMPLE_CALCULATION_PRIVATE_DATA *PrivateData = SIMPLE_CALCULATION_PRIVATE_DATA_FROM_THIS(This);

	PrivateData->FirstOperand  = A;
	PrivateData->SecondOperand = B;
	PrivateData->Result        = A - B;
	*Result                    = PrivateData->Result;

	Print(L"Subtract: %u - %u = %u\n", PrivateData->FirstOperand, PrivateData->SecondOperand, PrivateData->Result);

	return EFI_SUCCESS;
}

EFI_STATUS
EFIAPI
Multiply(IN EFI_SIMPLE_CALCULATION_PROTOCOL *This, IN UINTN A, IN UINTN B, OUT UINTN *Result)
{
	SIMPLE_CALCULATION_PRIVATE_DATA *PrivateData = SIMPLE_CALCULATION_PRIVATE_DATA_FROM_THIS(This);

	PrivateData->FirstOperand  = A;
	PrivateData->SecondOperand = B;
	PrivateData->Result        = A * B;
	*Result                    = PrivateData->Result;

	Print(L"Multiply: %u * %u = %u\n", PrivateData->FirstOperand, PrivateData->SecondOperand, PrivateData->Result);

	return EFI_SUCCESS;
}

EFI_STATUS
EFIAPI
Divide(IN EFI_SIMPLE_CALCULATION_PROTOCOL *This, IN UINTN A, IN UINTN B, OUT UINTN *Result)
{
	SIMPLE_CALCULATION_PRIVATE_DATA *PrivateData = SIMPLE_CALCULATION_PRIVATE_DATA_FROM_THIS(This);

	if (B == 0) {
		return EFI_INVALID_PARAMETER;
	}

	PrivateData->FirstOperand  = A;
	PrivateData->SecondOperand = B;
	PrivateData->Result        = A / B;
	*Result                    = PrivateData->Result;
	
	Print(L"Divide: %u / %u = %u\n", PrivateData->FirstOperand, PrivateData->SecondOperand, PrivateData->Result);

	return EFI_SUCCESS;
}

EFI_SIMPLE_CALCULATION_PROTOCOL *
EFIAPI
InitSimpleCalculationPrivate()
{
	SIMPLE_CALCULATION_PRIVATE_DATA *Private = &gSimpleCalculationPrivateData;

	Private->Signature                          = SIMPLE_CALCULATION_PRIVATE_DATA_SIGNATURE;
	Private->SimpleCalculationProtocol.Revision = 0x00010000;
	Private->SimpleCalculationProtocol.Add      = Add;
	Private->SimpleCalculationProtocol.Subtract = Subtract;
	Private->SimpleCalculationProtocol.Multiply = Multiply;
	Private->SimpleCalculationProtocol.Divide   = Divide;

	return &Private->SimpleCalculationProtocol;
}

EFI_STATUS
EFIAPI
SimpleCalculationEntryPoint(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
{
	SIMPLE_CALCULATION_PRIVATE_DATA *Private = &gSimpleCalculationPrivateData;

	InitSimpleCalculationPrivate();

	return gBS->InstallProtocolInterface(&ImageHandle, &gEfiSimpleCalculationProtocolGuid, EFI_NATIVE_INTERFACE, &Private->SimpleCalculationProtocol);
}